#ifndef _INTERFACE_HPP
#define _INTERFACE_HPP

#include <time.h>
#include <signal.h>
#include <pthread.h>
#include <stdint.h>

#include <string>
#include <vector>

#include <libxml++/libxml++.h>

void tokenize(const std::string& str,  const std::string& delimiters,
        std::vector<std::string>& tokens);

enum {
    FP_ENROLL,
    FP_IDENTIFY,
    FP_DELETE,
    FP_CHANGE_LEVEL
};

enum {
    SAMPLE,
    CONDITIONAL,
    EMPTY
};

enum {
    SLOW,
    FAST
};

enum {
    CONTACT,
    BUTTON,
    DIGITAL
};

struct Commands_info {
    int type;

    std::string expr;
    std::vector< std::pair<unsigned int, std::string> > if_true;
    std::vector< std::pair<unsigned int, std::string> > if_false;
    std::vector< std::pair<unsigned int, std::string> > sample;
};

struct Serial_info{
    int s_fd;
    pthread_mutex_t s_mutex;
};

/* for digital sensors */
typedef struct digital_s {
    uint8_t id;
    uint16_t value;
} digital_t;

/* base packet protocol structure defined by revotech */
typedef struct packet_s {
    uint8_t s_code;
    uint8_t addr;
    uint8_t size;
    uint8_t data[0];
} b_packet_t;

/* panel packet protocol structure defined by revotech */
typedef struct panel_packet_s {
    uint8_t s_code;
    uint8_t addr;
    uint8_t size;
    uint8_t cmd;
    uint8_t s[2]; /* 16 sensors packed in two bytes */
    uint8_t b[3]; /* 12 buttons packed in tree bytes */
    uint8_t data[0]; /* dynamically allocated array for the digital sensors and csum */
} p_packet_t;

class Base;
class Mode;
class Panel;
class Sensor;
class Command;
class Condition;

class Base {
    public:
        unsigned int gid;
        std::string name;
        std::string value;
        xmlpp::Attribute* attribute;

        pthread_mutex_t set_get_mutex;

        Base();

        virtual std::string get();
        virtual void set(std::string v);
};

class Sensor : public Base {
    public:
        uint8_t id;
        uint8_t type;
        int ivent_handler;

        std::string get();
        void set(std::string v);
};

class Panel : public Base {
    public:
        std::string port;
        std::string init_data;
        std::string update_data;

        std::vector<Sensor*> sensors;

        timer_t timerid;
        struct sigevent sevp;
        struct itimerspec timeout;
        struct timeval read_timeout;

        pthread_t thread;

        Panel() 
        { 
            this->sevp.sigev_notify = SIGEV_NONE; 
            timer_create(CLOCK_REALTIME, &this->sevp, &this->timerid); 

            // default read timeout;
            read_timeout.tv_sec = 0;
            read_timeout.tv_usec = 50000;
        }

        void init();
        void update();
        void start_timer();

        static void* update_routine(void* obj);

        std::string get();
        void set(std::string v);
};

class Command : public Base {
    public:
        bool mode;
        int start_byte;
        int bytes_to_get;
        std::string port;
        std::string data_for_set;
        std::string data_for_get;
        struct timeval read_timeout;
        std::vector<unsigned int> data_gids;

        std::vector< std::pair<unsigned int, unsigned int > > connections;

        Command();

        void init();
        std::string get();
        void set(std::string v);
};

class Condition {
    public:
        Mode *m;
        std::string expr;

        Commands_info true_cmds;
        Commands_info false_cmds;

        pthread_t thread;

        timer_t timerid;
        struct sigevent sevp;
        struct itimerspec timeout;

        timer_t step_timerid;
        struct sigevent step_sevp;
        struct itimerspec step_timeout;

        Condition();

        void stop();
        void start();

        bool is_timeout(timer_t &timerid);
        void start_timer(std::string t_str);
        static void* start_routine(void* obj);
};

class Function : public Base {
    public:
        std::string expr;
        std::vector<unsigned int> expr_gids;

        Commands_info true_cmds;
        Commands_info false_cmds;

        std::string get();
        void set(std::string v);
};

class Mode : public Base {
    public:
        std::vector<Condition> cnds;
        std::vector< std::pair<unsigned int, std::string> > actions;

        std::string state;
        int working_count;

        pthread_mutex_t v_mutex;
        pthread_mutex_t working_mutex;

        Mode();

        void stop();
        void start();

        void set(std::string v);
        std::string get();

        void set_value(std::string v);
        std::string get_value();

        void turn_off_mode();
};

class Timer : public Base {
    public:
        std::string state;
        std::vector< std::pair<unsigned int, std::string> > actions;

        pthread_mutex_t v_mutex;

        timer_t timerid;
        struct sigevent sevp;
        struct itimerspec timeout;

        pthread_t thread;
        pthread_mutex_t t_mutex;

        Timer() 
        { 
            pthread_mutex_init(&this->set_get_mutex, NULL);
            pthread_mutex_init(&this->v_mutex, NULL);
            pthread_mutex_init(&this->t_mutex, NULL);

            this->sevp.sigev_notify = SIGEV_NONE;
            timer_create(CLOCK_REALTIME, &this->sevp, &this->timerid);
        }

        void stop();
        void start();

        void set(std::string v);
        std::string get();

        void set_value(std::string v);
        std::string get_value();

        bool is_timeout();
        void start_timer();
        static void* start_routine(void* obj);

};

class Gsm : public Base {
    public:
        std::string number;
        std::string text;

        pthread_mutex_t v_mutex;

        timer_t timerid;
        struct sigevent sevp;
        struct itimerspec timeout;

        pthread_t thread;
        pthread_mutex_t t_mutex;

        std::string get();
        void set(std::string v);
        void send_msg();
};

class Fingerprint : public Base {
    public:
        int cmd_type;
        std::string get();
        void set(std::string value);
};

class SysSleep : public Base {
    public:
        std::string get();
        void set(std::string value);
};

#endif // INTERFACE_HPP
